package com.Register.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.StrUtil;
import com.Register.constant.*;
import com.Register.context.BaseContext;
import com.Register.dto.UserAppletLoginDTO;
import com.Register.dto.UserRegisterDTO;
import com.Register.dto.WxUserDTO;
import com.Register.entity.User;
import com.Register.entity.WxUser;
import com.Register.exception.*;
import com.Register.mapper.UserMapper;
import com.Register.mapper.WxUserMapper;
import com.Register.properties.WeChatProperties;
import com.Register.result.Result;
import com.Register.service.UserService;
import com.Register.utils.HttpClientUtil;
import com.Register.vo.CheckBindVO;
import com.Register.vo.WxUserVO;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import jakarta.annotation.Resource;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;

import static com.Register.constant.RedisConstant.expireTime;
import static com.Register.constant.RedisConstant.userInfo;


@Service
@Slf4j
@RequiredArgsConstructor
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {

    //微信接口服务地址
    public static final String WX_LOGIN = "https://api.weixin.qq.com/sns/jscode2session";


    @Resource
    private WeChatProperties weChatProperties;

    @Resource
    private RedissonClient redissonClient;


    @Resource
    private StringRedisTemplate stringRedisTemplate;

    @Resource
    private ObjectMapper objectMapper = new ObjectMapper();

    @Resource
    private WxUserMapper wxUserMapper;
    @Resource
    private  UserMapper userMapper;

    @Autowired
    private final PasswordEncoder passwordEncoder;



    /**
     * 微信登录
     * @param userAppletLoginDTO 传过来的DTO
     * @return wxUser
     */
    @Override
    //保证更新操作是原子性的，即要么全部更新成功，要么全部更新失败
    @Transactional
    public WxUser wxLogin(UserAppletLoginDTO userAppletLoginDTO) {

        System.out.println("userAppletLoginDTO:"+userAppletLoginDTO);

        //调用微信接口服务，获得当前微信用户的openid
        String openid = getOpenid(userAppletLoginDTO.getCode());
        System.out.println("当前微信用户的openid: "+openid);

        //判断openid是否为空，如果为空表示登录失败，抛出业务异常
        if (openid==null){
            throw new LoginFailedException(MessageConstant.LOGIN_FAILED);
        }


        //判断当前用户是否为新用户
        WxUser wxUser = wxUserMapper.selectByOpenid(openid);

        //判断这个账号是否启用
        if(wxUser!=null&&wxUser.getDeleted()==1){
            throw new LoginFailedException(MessageConstant.USER_STATUS_BAN);
        }
        //如果为新用户，自动完成注册
        if (wxUser==null){
            RLock lock = redissonClient.getLock(RedissonConstant.LOCK_USER_REGISTER_KEY + openid);
            if (!lock.tryLock()){
                throw new RegisterFailedException(MessageConstant.USER_EXIST);
            }
            try {
                log.info("注册新用户");
                wxUser=WxUser.builder()
                        .openid(openid)
                        //默认禁用，等通过和web端账号绑定后启用
                        .status(StatusConstant.DISABLE)
                        //默认username=nullUsername,password=nullPassword,name=nullName
                        .username(DefaultUserValueConstant.USERNAME)
                        .password(DefaultUserValueConstant.PASSWORD)
                        .name(DefaultUserValueConstant.NAME)
                        .phone(DefaultUserValueConstant.PHONE)
                        .avatar(DefaultUserValueConstant.AVATAR)
                        .status(StatusConstant.DISABLE)
                        .createTime(LocalDateTime.now())
                        .build();
                wxUserMapper.insert(wxUser);
            }catch (DuplicateKeyException e){
                throw new RegisterFailedException(MessageConstant.REGISTER_FAILED);
            }finally {
                lock.unlock();
            }

        }
        //返回这个用户的对象

        return wxUser;
    }

    /**
     * 调用微信接口服务，获取微信用户的openid
     * @param code 前端传过来小程序登录的code
     * @return openid
     */
    private String getOpenid(String code){
        //调用微信接口服务，获得当前微信用户的openid
        Map<String, String> map = new HashMap<>();
        map.put("appid",weChatProperties.getAppid());
        map.put("secret",weChatProperties.getSecret());
        map.put("js_code",code);
        map.put("grant_type","authorization_code");
        System.out.println(map);
        String json = HttpClientUtil.doGet(WX_LOGIN, map);

        JSONObject jsonObject = JSON.parseObject(json);

        return jsonObject.getString("openid");
    }

    /**
     * 查询用户个人信息
     * @return
     * @throws JsonProcessingException
     */
    @Override
    public WxUserVO getUserInfo() throws JsonProcessingException {
        //获取id查询用户信息
        Long userId=BaseContext.getCurrentId();
        // 创建返回结果对象
        WxUserVO userVO = new WxUserVO();
        //构建缓存键名
        String cacheKey=userInfo+userId;
        //先查询缓存中有没有对应的缓存
        String result=stringRedisTemplate.opsForValue().get(cacheKey);
        //如果在缓存中查到了数据
        if(StrUtil.isNotBlank(result)){
            //将查到的数据序列化后返回
            return objectMapper.readValue(result,WxUserVO.class);
        }
        //如果缓存中没有到数据库中查询,因为登录后才能调用此方法，且用户无法传参，内存穿透问题可以忽略
        else {
            //数据库中查询
            WxUser user = wxUserMapper.selectById(userId);
            //属性复制，返回
            BeanUtils.copyProperties(user, userVO);
            String userString=objectMapper.writeValueAsString(userVO);
            //缓存中存入对应数据,并设置过期时间
            stringRedisTemplate.opsForValue().set(cacheKey,userString,expireTime, TimeUnit.MINUTES);
            return userVO;
        }
    }

    /**
     * 检查用户是否绑定，已绑定返回1，未绑定返回0
     * @return 返回绑定信息，已绑定返回1，未绑定返回0
     */
    @Override
    public CheckBindVO checkBind() {

        //通过id查询用户信息
        Long userId = BaseContext.getCurrentId();

        CheckBindVO checkBindVO=new CheckBindVO();

        WxUser wxUser = wxUserMapper.selectById(userId);

        //已绑定逻辑
        checkBindVO.setIsBind(1);

        //判断是否绑定
        if (wxUser.getHasBind()==0L){
            //未绑定逻辑，覆盖之前信息
            checkBindVO.setIsBind(0);
        }

        return checkBindVO;
    }



    /**
     * 用户注册接口
     */
    @Override
    public Result<String> userRegister(UserRegisterDTO userRegisterDTO) {
        userRegisterDTO.setPassword(passwordEncoder.encode(userRegisterDTO.getPassword()));
        //判断使用id查询用户
        QueryWrapper<WxUser>wrapper=new QueryWrapper<>();
        Long id=BaseContext.getCurrentId();
        wrapper.lambda()
                .eq(WxUser::getId,id);
        log.info("用户id是:{}",id);
        WxUser user=wxUserMapper.selectOne(wrapper);
        if(user.getHasBind()==1L){
            return Result.success("您已经注册无需再次注册");
        }
        //拷贝属性
        BeanUtils.copyProperties(userRegisterDTO,user);
        user.setHasBind(1);
        int result=wxUserMapper.updateById(user);
        return Result.success("注册成功");
    }

    /**
     * 通过用户名检查用户是否存在
     * @param username
     * @return
     */
    public User checkUserExist(String username)  {
        //通过web端账号密码查询用户信息
        //1、判断用户是否存在
        User user = userMapper.selectByUsername(username);
        if (user==null){
            throw new AccountNotFoundException(MessageConstant.ACCOUNT_NOT_FOUND);
        }
        return user;
    }


    /**
     * 检查用户是否存在
     * @param username
     * @return
     */
    @Override
    public WxUser checkUserExists(String username) {
        //判断使用用户名查询用户
        QueryWrapper<WxUser>wrapper=new QueryWrapper<>();
        wrapper.lambda()
                .eq(WxUser::getUsername,username);
        return wxUserMapper.selectOne(wrapper);
    }

    /*
    微信用户更新信息接口
     */
    @Override
    public Result<String> wxUserUpdate(WxUserDTO wxUserDTO) {
        WxUser wxUser=wxUserMapper.selectById(BaseContext.getCurrentId());
        wxUser.setUpdateTime(LocalDateTime.now());
        BeanUtil.copyProperties(wxUserDTO,wxUser);
        wxUserMapper.updateById(wxUser);
        //构建缓存键名
        String cacheKey=userInfo+BaseContext.getCurrentId();
        stringRedisTemplate.delete(cacheKey);
        return Result.success("更新成功");
    }
}
